/**
 * Copyright (C) 2010 altuure <altuure [AT] gmail [DOT] com> http://www.altuure.com/projects/yagdao
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.altuure.yagdao.spring.jpa;

import java.io.InputStream;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.altuure.yagdao.YLimit;
import com.altuure.yagdao.base.DAOTestHelper;
import com.altuure.yagdao.dao.OrderDAO;
import com.altuure.yagdao.dao.OrderItemDAO;
import com.altuure.yagdao.dao.ProductDAO;
import com.altuure.yagdao.dao.SimpleBeanDAO;

public class SpringJpaDAOTestHelper extends DAOTestHelper {
    public static final Log log = LogFactory.getLog(SpringJpaDAOTestHelper.class);

    protected EntityManagerFactory entityManagerFactory;

    private ClassPathXmlApplicationContext context;
    private static SpringJpaDAOTestHelper instance = null;

    private JpaTransactionManager transactionManager;

    private JpaTemplate jpaTemplate;

    private TransactionStatus transaction;

    private SpringJpaDAOTestHelper() {
        super();
        InputStream resourceAsStream = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("spring/jpa/spring-context.xml");
        context = new ClassPathXmlApplicationContext("spring/jpa/spring-context.xml");
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String string : beanDefinitionNames) {
            System.out.println("bean:" + string);
        }
        try {
            init();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static synchronized final SpringJpaDAOTestHelper getInstance() {
        if (instance == null)
            instance = new SpringJpaDAOTestHelper();
        return instance;
    }

    @Override
    protected void initDAO() throws Exception {
        orderDAO = (OrderDAO) context.getBean(OrderDAO.class);
        orderItemDAO = (OrderItemDAO) context.getBean(OrderItemDAO.class);
        productDAO = (ProductDAO) context.getBean(ProductDAO.class);
        simpleBeanDAO = (SimpleBeanDAO) context.getBean(SimpleBeanDAO.class);
    }

    @Override
    protected void initDB() throws Exception {
        if (entityManagerFactory != null)
            return;
        entityManagerFactory = context.getBean(EntityManagerFactory.class);
        transactionManager = context.getBean(JpaTransactionManager.class);
        jpaTemplate = context.getBean(JpaTemplate.class);
        jpaTemplate.setExposeNativeEntityManager(true);

    }

    @Override
    protected void beginTx() {
        transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());

    }

    @Override
    protected void commitTx() {
        transactionManager.commit(transaction);

        // getEntityManager().close();

    }

    @Override
    protected void rollbackTx() {

        transactionManager.rollback(transaction);
        // getEntityManager().clear();

    }

    @Override
    public void save(Object object) {
        getEntityManager().persist(object);

    }

    @Override
    public Object executePageQuery(String queryString, YLimit page, Object... args) {
        Query query = getEntityManager().createQuery(queryString);
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                Object object = args[i];
                query.setParameter(i + 1, object);
            }
        }
        if (page != null && page.getFirstResult() > 0)
            query.setFirstResult(page.getFirstResult());
        if (page != null && page.getPageSize() > 0)
            query.setMaxResults(page.getPageSize());
        List list = query.getResultList();
        if (list.isEmpty())
            log.warn(" queryReturns empty:" + queryString);

        return list;
    }

    @Override
    public Object executePageQuery(String queryString, YLimit page, Map<String, Object> args) {
        Query query = getEntityManager().createQuery(queryString);
        for (String key : args.keySet()) {

            query.setParameter(key, args.get(key));
        }
        if (page != null && page.getFirstResult() > 0)
            query.setFirstResult(page.getFirstResult());
        if (page != null && page.getPageSize() > 0)
            query.setMaxResults(page.getPageSize());
        List list = query.getResultList();
        if (list.isEmpty())
            log.warn(" queryReturns empty:" + queryString);

        return list;
    }

    public Object executeSingleQuery(String queryString, Object... args) {
        Query query = getEntityManager().createQuery(queryString);
        if (args != null) {
            for (int i = 0; i < args.length; i++) {
                Object object = args[i];
                query.setParameter(i + 1, object);
            }
        }

        return query.getSingleResult();
    }

    public Object executeSingleQuery(String queryString, Map<String, Object> args) {
        Query query = getEntityManager().createQuery(queryString);
        for (String key : args.keySet()) {

            query.setParameter(key, args.get(key));
        }

        return query.getSingleResult();
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        new SpringJpaDAOTestHelper();

    }

    @Override
    public Object get(Class clz, Long id) {
        return getEntityManager().find(clz, id);
    }

    @Override
    public Object executeNamedQuery(String queryString, YLimit page, Map<String, Object> args) {
        Query query = getEntityManager().createNamedQuery(queryString);
        for (String key : args.keySet()) {

            query.setParameter(key, args.get(key));
        }
        if (page != null && page.getFirstResult() > 0)
            query.setFirstResult(page.getFirstResult());
        if (page != null && page.getPageSize() > 0)
            query.setMaxResults(page.getPageSize());
        List list = query.getResultList();
        if (list.isEmpty())
            log.warn(" queryReturns empty:" + queryString);

        return list;
    }

    @Override
    public Object executeNamedQuery(String queryString, YLimit page, Object... args) {
        Query query = getEntityManager().createNamedQuery(queryString);
        for (int i = 0; i < args.length; i++) {
            Object object = args[i];
            query.setParameter(i + 1, object);
        }
        if (page != null && page.getFirstResult() > 0)
            query.setFirstResult(page.getFirstResult());
        if (page != null && page.getPageSize() > 0)
            query.setMaxResults(page.getPageSize());
        List list = query.getResultList();
        if (list.isEmpty())
            log.warn(" queryReturns empty:" + queryString);

        return list;
    }

    private EntityManager getEntityManager() {

        EntityManager entityManager = EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory,
                null);
        return entityManager;
    }

}
